home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
lang
/
hfiles01
/
hfiles.c
< prev
next >
Wrap
Text File
|
1994-09-21
|
14KB
|
696 lines
/*
* hfiles.c
*
* This program reads through source files, generating a list of
* dependencies suitable for inclusion in a makefile. It can handle
* C-style include syntax (#include "file") or assembler-style
* syntax (include file), and can be easily modified to support other
* syntaxes.
*
* This code is copyright (c) 1994, Kevin D. Hunter. It is expressly
* placed in the public domain for non-commercial use, without fee.
* Commercial redistribution of this source code, or object code derived
* from it is not permitted.
*
* Please use this code in the spirit in which it is intended. If you
* have suggestions for improvements, I would love to hear them.
* I can be reached as hunterkd@aol.com
*
* Known deficiencies:
* - If you use a command file (@file), you must put only one option,
* file or whatever on each line. In addition, wild cards are not
* expanded from within command file.
*
* - There is no support for include paths. Right now, if an include
* file is not in the current directory and does not have path info
* in the include line, the program will not include it in the
* dependency list, and will print a warning about not being able
* to open the file.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FALSE 0
#define TRUE 1
#define LINE_BUFFER_SIZE 256
#define DEFAULT_ALLOC 16
#define MAX_FILE_LEN 12
typedef struct sExtension
{
char * srcExt; /* Extension of the source file */
size_t srcExtLen; /* Length of the source file extension */
char * objExt; /* Extension of the corresponding object file */
size_t objExtLen; /* Length of the object file extension */
unsigned int scanner; /* Which scanner type to use */
} Extension;
#define SCANNER_EOL 0
#define SCANNER_CTYPE 1 /* "C" style */
#define SCANNER_ATYPE 2 /* ASM style */
/*
* This table has both upper and lower case versions of the extensions,
* because the Microsoft "SETARGV.OBJ" (the command line wild-card expander)
* has a nasty tendency to put file names into upper case. If you use
* this in another environment, you may need to fix this.
*/
const Extension ExtList[] =
{
{ ".c", 2, ".obj", 4, SCANNER_CTYPE },
{ ".cc", 3, ".obj", 4, SCANNER_CTYPE },
{ ".cxx", 4, ".obj", 4, SCANNER_CTYPE },
{ ".cpp", 4, ".obj", 4, SCANNER_CTYPE },
{ ".C", 2, ".OBJ", 4, SCANNER_CTYPE },
{ ".CC", 3, ".OBJ", 4, SCANNER_CTYPE },
{ ".CXX", 4, ".OBJ", 4, SCANNER_CTYPE },
{ ".CPP", 4, ".OBJ", 4, SCANNER_CTYPE },
{ ".asm", 4, ".obj", 4, SCANNER_ATYPE },
{ ".s", 2, ".obj", 4, SCANNER_ATYPE },
{ ".ASM", 4, ".OBJ", 4, SCANNER_ATYPE },
{ ".S", 2, ".OBJ", 4, SCANNER_ATYPE },
{ ".rc", 3, ".res", 4, SCANNER_CTYPE },
{ ".RC", 3, ".RES", 4, SCANNER_CTYPE },
{ "", 0, "", 0, SCANNER_EOL }
};
/*
* This is where the default values for options are set.
*/
unsigned int TabSize = 4;
unsigned int LineWidth = 80;
unsigned int FileSlotsAvail = 0;
unsigned int FileSlotsUsed = 0;
char **FileSlot = (char **)NULL;
/*
* This routine strips the trailing \n (or \r\n) off a line. If no
* \n or \r\n is found, the input line must have been truncated by
* fgets, so it returns FALSE to indicate that it wasn't a "whole"
* line.
*/
int
WholeLine(char *p)
{
while(*p != '\0')
{
if (*p == '\r' || *p == '\n')
{
*p = '\0';
return(TRUE);
}
++p;
}
return(FALSE);
}
/*
* This routine initializes the dependencies storage stuff. This is
* a dynamically allocated and extended list of pointers, each of which
* contains a dynamically allocated string (file name).
*/
int
InitDependencies(void)
{
unsigned int i;
FileSlot = malloc(DEFAULT_ALLOC * sizeof(char *));
if (FileSlot == (char **)NULL)
{
(void)fprintf(stderr, "Out of memory\n");
return(FALSE);
}
for (i = 0; i < DEFAULT_ALLOC; i++)
{
FileSlot[i] = (char *)NULL;
}
FileSlotsUsed = 0;
FileSlotsAvail = DEFAULT_ALLOC;
return(TRUE);
}
/*
* This routine clears out accumulated data in the dependencies area.
* It doesn't delete the list itself, only the attached strings.
*/
void
ClearDependencies(void)
{
unsigned int i;
if (FileSlot == (char **)NULL)
{
return;
}
for (i = 0; i < FileSlotsUsed; i++)
{
if (FileSlot[i] != (char *)NULL)
{
free(FileSlot[i]);
FileSlot[i] = (char *)NULL;
}
}
FileSlotsUsed = 0;
}
/*
* This routine frees ALL the memory associated with the dependencies
* area.
*/
void
ExitDependencies(void)
{
if (FileSlot == (char **)NULL)
{
return;
}
ClearDependencies();
free(FileSlot);
FileSlot = (char **)NULL;
}
/*
* This file adds a file name to the current dependency list, provided
* it is not already in the list. It returns TRUE if successful, FALSE
* if out of memory.
*/
int
AddDependency(const char *pFile)
{
char **newList;
unsigned int i;
assert(FileSlot != (char **)NULL);
/*
* Check the existing slots for a match
*/
for (i = 0; i < FileSlotsUsed; i++)
{
if (strcmp(FileSlot[i], pFile) == 0)
{
return(TRUE);
}
}
/*
* If we've used all the slots (the > part of the test is just paranoia)
* extend the list.
*/
if (FileSlotsUsed >= FileSlotsAvail)
{
newList = (char **)realloc(FileSlot, (FileSlotsAvail + DEFAULT_ALLOC) * sizeof(char*));
if (newList == (char **)NULL)
{
(void)fprintf(stderr, "Out of memory\n");
return(FALSE);
}
FileSlot = newList;
for (i = FileSlotsAvail; i < FileSlotsAvail + DEFAULT_ALLOC; i++)
{
FileSlot[i] = (char *)NULL;
}
FileSlotsAvail += DEFAULT_ALLOC;
}
/*
* Tack a copy of the filename into the next available slot
*/
FileSlot[FileSlotsUsed] = malloc(strlen(pFile) + 1);
if (FileSlot[FileSlotsUsed] == (char *)NULL)
{
(void)fprintf(stderr, "Out of memory\n");
return(FALSE);
}
(void)strcpy(FileSlot[FileSlotsUsed], pFile);
FileSlotsUsed++;
return(TRUE);
}
/*
* This routine generates the formatted output
*/
void
GenerateOutput(const char *pFile, const Extension *pExt)
{
unsigned int n;
unsigned int depCol;
unsigned int curCol;
size_t i;
size_t len;
depCol = ((MAX_FILE_LEN + 2 + TabSize - 1) / TabSize) * TabSize;
len = strlen(pFile) - pExt->srcExtLen;
curCol = 0;
for (i = 0; i < len; i++)
{
putchar(pFile[i]);
curCol++;
}
(void)fputs(pExt->objExt, stdout);
curCol += pExt->objExtLen;
putchar(':');
curCol++;
while(curCol < depCol)
{
putchar('\t');
curCol = ((curCol + TabSize) / TabSize) * TabSize;
}
for (i = 0; i < len; i++)
{
putchar(pFile[i]);
curCol++;
}
(void)fputs(pExt->srcExt, stdout);
curCol += pExt->srcExtLen;
for (n = 0; n < FileSlotsUsed; n++)
{
len = strlen(FileSlot[n]);
if (curCol + len + 1 >= LineWidth - 2)
{
putchar(' ');
putchar('\\');
putchar('\n');
curCol = 0;
while(curCol < depCol)
{
putchar('\t');
curCol = ((curCol + TabSize) / TabSize) * TabSize;
}
}
else
{
putchar(' ' );
}
(void)fputs(FileSlot[n], stdout);
curCol += len + 1;
}
putchar('\n');
}
/*
* This is the "C" syntax file scanner. It looks for lines containg
* #include "file". Lines of the format #include <file> are ignored.
* New dependencies found are tacked on the end of the list, and then
* This list is itself parsed. Because of this approach, it never
* recurses more than two levels. At most, the list grows ahead of the
* point at which it is processing.
*/
int
ScanFileC(const char *pFile, int nested, const Extension *pExt)
{
FILE *fp;
char lineBuffer[LINE_BUFFER_SIZE + 1];
long lineNum;
char *pStart;
char *pEnd;
unsigned int i;
if (!nested)
{
ClearDependencies();
}
fp = fopen(pFile, "r");
if (fp == (FILE *)NULL)
{
if (nested)
{
(void)fprintf(stderr, "hfiles: Warning: can't open '%s'\n", pFile);
return(TRUE);
}
else
{
(void)fprintf(stderr, "hfiles: Can't open '%s'\n", pFile);
return(FALSE);
}
}
lineNum = 0;
while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp) != (char *)NULL)
{
++lineNum;
if (!WholeLine(lineBuffer))
{
(void)fprintf(stderr,
"hfiles: '%s' line %ld too long\n",
pFile,
lineNum);
return(FALSE);
}
pStart = lineBuffer;
while(*pStart == ' ' || *pStart == '\t')
{
++pStart;
}
if (strncmp(pStart, "#include", 8) == 0)
{
pStart += 8;
while(*pStart == ' ' || *pStart == '\t')
{
++pStart;
}
if (*pStart == '"')
{
++pStart;
pEnd = pStart;
while(*pEnd != '"' && *pEnd != '\0')
{
++pEnd;
}
if (*pEnd == '"')
{
*pEnd = '\0';
if (!AddDependency(pStart))
{
(void)fclose(fp);
return(FALSE);
}
}
}
}
}
(void)fclose(fp);
if (nested)
{
return(TRUE);
}
for (i = 0; i < FileSlotsUsed; i++)
{
if (!ScanFileC(FileSlot[i], TRUE, pExt))
{
return(FALSE);
}
}
GenerateOutput(pFile, pExt);
return(TRUE);
}
/*
* This is the assembly format processor. It looks for lines like:
* include file
* and accepts either "include" or "INCLUDE".
*/
int
ScanFileA(const char *pFile, int nested, const Extension *pExt)
{
FILE *fp;
char lineBuffer[LINE_BUFFER_SIZE + 1];
long lineNum;
char *pStart;
char *pEnd;
unsigned int i;
if (!nested)
{
ClearDependencies();
}
fp = fopen(pFile, "r");
if (fp == (FILE *)NULL)
{
if (nested)
{
(void)fprintf(stderr, "hfiles: Warning: can't open '%s'\n", pFile);
return(TRUE);
}
else
{
(void)fprintf(stderr, "hfiles: Can't open '%s'\n", pFile);
return(FALSE);
}
}
lineNum = 0;
while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp) != (char *)NULL)
{
++lineNum;
if (!WholeLine(lineBuffer))
{
(void)fprintf(stderr,
"hfiles: '%s' line %ld too long\n",
pFile,
lineNum);
return(FALSE);
}
pStart = lineBuffer;
while(*pStart == ' ' || *pStart == '\t')
{
++pStart;
}
if (strncmp(pStart, "include", 7) == 0 ||
strncmp(pStart, "INCLUDE", 7) == 0)
{
pStart += 7;
while(*pStart == ' ' || *pStart == '\t')
{
++pStart;
}
pEnd = pStart;
while(*pEnd != ' ' && *pEnd != '\t' && *pEnd != ';' && *pEnd != '\0')
{
++pEnd;
}
*pEnd = '\0';
if (!AddDependency(pStart))
{
(void)fclose(fp);
return(FALSE);
}
}
}
(void)fclose(fp);
if (nested)
{
return(TRUE);
}
for (i = 0; i < FileSlotsUsed; i++)
{
if (!ScanFileA(FileSlot[i], TRUE, pExt))
{
return(FALSE);
}
}
GenerateOutput(pFile, pExt);
return(TRUE);
}
/*
* This routine processes command line arguments and arguments received
* from command files. These items can be file names or options.
*/
int
ProcessArgument(const char *pArg)
{
FILE *fp;
const Extension *pExt;
size_t len;
char lineBuffer[LINE_BUFFER_SIZE + 1];
long lineNum;
while(*pArg == ' ' || *pArg == '\t')
{
++pArg;
}
if (*pArg == '@')
{
fp = fopen(&pArg[1], "r");
if (fp == (FILE *)NULL)
{
(void)fprintf(stderr, "hfiles: Can't open '%s'\n", &pArg[1]);
return(FALSE);
}
lineNum = 0;
while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp) != (char *)NULL)
{
++lineNum;
if (!WholeLine(lineBuffer))
{
(void)fprintf(stderr,
"hfiles: '%s' line %ld too long\n",
&pArg[1],
lineNum);
return(FALSE);
}
if (!ProcessArgument(lineBuffer))
{
(void)fclose(fp);
return(FALSE);
}
}
(void)fclose(fp);
return(TRUE);
}
if (*pArg == '-')
{
switch(pArg[1])
{
case 't': case 'T':
if (sscanf(&pArg[2], "%u", &TabSize) != 1)
{
(void)fprintf(stderr, "Invalid tab size: '%s'\n", &pArg[2]);
return(FALSE);
}
break;
case 'l': case 'L':
if (sscanf(&pArg[2], "%u", &LineWidth) != 1)
{
(void)fprintf(stderr, "Invalid line width: '%s'\n", &pArg[2]);
return(FALSE);
}
break;
default:
(void)fprintf(stderr, "Unknown option: '%s'\n", pArg);
return(FALSE);
}
return(TRUE);
}
len = strlen(pArg);
for(pExt = ExtList; pExt->scanner != SCANNER_EOL; pExt++)
{
if (len > pExt->srcExtLen)
{
if (strcmp(&pArg[len - pExt->srcExtLen], pExt->srcExt) == 0)
{
switch(pExt->scanner)
{
case SCANNER_CTYPE:
return(ScanFileC(pArg, FALSE, pExt));
case SCANNER_ATYPE:
return(ScanFileA(pArg, FALSE, pExt));
default:
assert(FALSE);
}
}
}
}
(void)fprintf(stderr, "'%s' does not have a recognized extension\n", pArg);
return(FALSE);
}
void
Usage(void)
{
(void)fprintf(stderr, "Usage:\thfiles [options] files...\n");
(void)fprintf(stderr, "\tOptions:\t-tN\tN char tab stops\n");
(void)fprintf(stderr, "\t\t\t-lN\tOutput line width of N chars\n");
(void)fprintf(stderr, "\t\t\t@file\tTake arguments from file\n");
}
int
main(int argc, char **argv)
{
int i;
if (argc == 1)
{
Usage();
return(1);
}
if (!InitDependencies())
{
return(1);
}
for (i = 1; i < argc; i++)
{
if (!ProcessArgument(argv[i]))
{
return(1);
}
}
ExitDependencies();
return(0);
}